package org.example.service.Impl;

import com.alibaba.fastjson.JSON;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import org.example.commonapi.domain.R;
import org.example.commonapi.domain.onlineCode.Code;
import org.example.commonapi.domain.onlineCode.CodeTopic;
import org.example.commonapi.domain.onlineCode.CommitOwn;
import org.example.commonapi.domain.onlineCode.SimpleJson;
import org.example.commonapi.domain.onlineCode.Tag;
import org.example.commonapi.domain.onlineCode.TestGroup;
import org.example.commonapi.dto.onlineCode.CodeTopicDTO;
import org.example.commonapi.mongo.CodeRepository;
import org.example.commonapi.mongo.CodeTopicRepository;
import org.example.commonapi.mongo.CommitOwnRepository;
import org.example.commonapi.mongo.TagRepository;
import org.example.commonapi.mongo.TestGroupRepository;
import org.example.service.OnlineCodeService;
import org.example.utils.OkHttpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class OnlineCodeServiceImpl implements OnlineCodeService {

    @Autowired
    private CodeTopicRepository codeTopicRepository;

    @Autowired
    private CodeRepository codeRepository;

    @Autowired
    private TestGroupRepository testGroupRepository;

    @Autowired
    private TagRepository tagRepository;

    @Autowired
    private CommitOwnRepository commitOwnRepository;

    @Autowired
    private OkHttpUtil client;

    private static Set<String> set = new HashSet<>();

    static {
        set.add("基本");
        set.add("算法");
        set.add("基础数据结构");
        set.add("高级数据结构");
        set.add("技巧");
        set.add("数学");
        set.add("其他");
        set.add("题目来源");
    }

    @Value("${onlinecode.url}")
    private String baseUrl;
    private static final MediaType MEDIA_TYPE_OCTET_STREAM = MediaType.parse("application/octet-stream");
    
    @Override
    public R<Object> uploadProblems(CodeTopic codeTopics) {
        int count = (int) codeTopicRepository.count();
        codeTopics.setCodeId(count + 1);
        if (codeTopics.getDifficulty() == 1) {
            codeTopics.setDifficultyName("简单");
        }
        if (codeTopics.getDifficulty() == 2 || codeTopics.getDifficulty() == 3) {
            codeTopics.setDifficultyName("中等");
        }
        if (codeTopics.getDifficulty() == 4 || codeTopics.getDifficulty() == 5) {
            codeTopics.setDifficultyName("困难");
        }
        codeTopics.setIsBan(0);
        codeTopics.setIsCreated(false);
        codeTopics.setCommitNum(0);
        codeTopics.setPassNum(0);
        codeTopics.setTimeLimit("1.00ms");
        codeTopics.setMemoryLimit("128MB");
        CodeTopic save = codeTopicRepository.save(codeTopics);
        return new R<>(save,200, "success");
    }

    @Override
    public R<Object> uploadTestGroup(TestGroup testGroup) throws IOException {
        boolean present = codeTopicRepository.findById(testGroup.getProblemId()).isPresent();
        if (!present) {
            return new R<>(null,400, "题目不存在");
        }
        // 从MultipartFile获取字节数组和文件名
        byte[] fileBytes = testGroup.getFile().getBytes(); // 假设 MultipartFile 有一个 getBytes 方法来获取字节数组
        String fileName = testGroup.getFile().getOriginalFilename(); // 假设 MultipartFile 有一个 getOriginalFilename 方法来获取文件名

        if (testGroup.getContent() == null) {
            testGroup.setContent("");
        }
        RequestBody fileBody = RequestBody.create(MEDIA_TYPE_OCTET_STREAM, fileBytes);
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("type", testGroup.getType())
                .addFormDataPart("content", testGroup.getContent())
                .addFormDataPart("file", fileName, fileBody)
                .build();

        String response = client.uploadFormData(baseUrl + "/uploadFile", requestBody);
        SimpleJson json = JSON.parseObject(response, SimpleJson.class);
        if (json.getCode() != 200) {
            return new R<>(null,json.getCode(), json.getMsg());
        } else {
            String jsons = null;
            if ("file".equals(testGroup.getType())) {
                jsons = convertMultiPartToString(testGroup.getFile());
            }
            TestGroup testGroupM = new TestGroup(jsons, testGroup.getProblemId(), json.getData().toString());
            TestGroup save = testGroupRepository.save(testGroupM);
            return new R<>( save,200, json.getMsg());
        }
    }

    @Override
    public R<Object> uploadCode(Code code) {
        Optional<CodeTopic> byId = codeTopicRepository.findById(code.getProblemId());
        if (byId.isEmpty()) {
            return new R<>(null,400, "题目不存在");
        }
        Optional<TestGroup> byId1 = testGroupRepository.findById(code.getTestGroupMongoId());
        if (byId1.isEmpty()) {
            return new R<>(null,400, "测试集不存在");
        }
        TestGroup testGroup = byId1.get();
        if (!Objects.equals(testGroup.getProblemId(), code.getProblemId())) {
            return new R<>(null,400, "测试集与题目不匹配");
        }

        String post = client.post(baseUrl + "/standardCode", JSON.toJSONString(code));
        SimpleJson json = JSON.parseObject(post, SimpleJson.class);
        if (json.getCode() != 200) {
            return new R<>(null,json.getCode(), json.getMsg());
        } else {
            code.setCodeId(json.getData().toString());
            Code save = codeRepository.save(code);
            CodeTopic codeTopic = byId.get();
            codeTopic.setIsCreated(true);
            codeTopicRepository.save(codeTopic);
            return new R<>(save,200, json.getMsg());
        }
    }

    @Transactional(readOnly = true)
    @Override
    public R<Object> getProblemsList(Integer page, Integer size, List<String> tags, String difficulty, String title) {
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<CodeTopic> codeTopicsPage;

        // 如果title不为空，则进行模糊查询
        if (title != null && !title.isEmpty()) {
            codeTopicsPage = codeTopicRepository.findByIsBanAndIsCreatedAndTitleContaining(0, true, ".*" + title + ".*", pageable);
        } else {
            // 如果没有提供title，执行普通查询
            codeTopicsPage = codeTopicRepository.findByIsBanAndIsCreated(0, true, pageable);
        }
        // 获取所有CommitOwn，并存入到 Set中
        Set<String> commitSet = commitOwnRepository.findAll()
                .stream()
                .map(CommitOwn::getProblemId)
                .collect(Collectors.toSet());

        // 将CodeTopic转换为DTO
        List<CodeTopicDTO> list = codeTopicsPage.getContent().stream()
                .map(codeTopic -> new CodeTopicDTO(codeTopic, commitSet.contains(codeTopic.getId())))
                .collect(Collectors.toList());

        // 标签筛选
        if (tags != null && !tags.isEmpty()) {
            list = list.stream()
                    .filter(dto -> new HashSet<>(dto.getTags()).containsAll(tags))
                    .collect(Collectors.toList());
        }

        // 难度筛选
        if (difficulty != null && !difficulty.isEmpty()) {
            list = list.stream()
                    .filter(dto -> difficulty.equals(dto.getDifficultyName()))
                    .collect(Collectors.toList());
        }

        return new R<>(list,200, "Success");
    }

    @Override
    public R<Object> getProblemsCount() {
        long count = codeTopicRepository.count();
        return new R<>(count,200, "success");
    }

    @Override
    public R<Object> getProblemsOne(String id) {
        Optional<CodeTopic> byId = codeTopicRepository.findById(id);
        return byId.<R<Object>>map(codeTopic -> new R<>(codeTopic,200, "success")).orElseGet(() -> new R<>(null,400, "题目不存在"));
    }

    @Override
    public R<String> deleteProblems(String id) {
        CodeTopic codeTopic = codeTopicRepository.findById(id).orElse(null);
        if (codeTopic == null) {
            return new R<>(null,400, "题目不存在");
        }
        codeTopic.setIsBan(1);
        codeTopicRepository.save(codeTopic);
        return new R<>(null,200, "禁用成功");
    }

    @Override
    public R<String> recoverProblems(String id) {
        CodeTopic codeTopic = codeTopicRepository.findById(id).orElse(null);
        if (codeTopic == null) {
            return new R<>(null,400, "题目不存在");
        }
        codeTopic.setIsBan(0);
        codeTopicRepository.save(codeTopic);
        return new R<>(null,200, "恢复成功");
    }

    @Override
    public R<Object> createTag(String tag, String tagType) {
        Tag tag1 = new Tag();
        tag1.setTag(tag);
        List<Tag> all = tagRepository.findAll();
        for (Tag tag2 : all) {
            if (tag2.getTag().equals(tag)) {
                return new R<>(null,400, "标签已存在");
            }
        }
        if (!set.contains(tagType)) {
            return new R<>(null,400, "标签不合法");
        }
        tag1.setTagType(tagType);
        tag1.setValue(all.size() + 1);
        tagRepository.save(tag1);
        return new R<>(tag1,200, "success");
    }

    @Override
    public R<Object> getTag() {
        List<Tag> all = tagRepository.findAll();
        return new R<>(all,200, "success");
    }

    public static String convertMultiPartToString(MultipartFile file) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader bufferedReader =
                     new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }
        return stringBuilder.toString();
    }
}
